package com.authService.service.impl;

import com.authService.convert.MenuConvert;
import com.authService.entity.SysMenuEntity;
import com.authService.mapper.SysMenuMapper;
import com.authService.pojo.req.MenuEditReqDTO;
import com.authService.pojo.req.MenuReqDTO;
import com.authService.pojo.req.MenuUpdateStatusReqDTO;
import com.authService.pojo.res.MenuDetailResDTO;
import com.authService.pojo.res.MenuResDTO;
import com.authService.service.ISysMenuService;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xiaopeng.common.page.PageUtil;
import com.xiaopeng.common.page.PageVO;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 系统菜单表 服务实现类
 * </p>
 *
 * @author xiaopeng
 * @since 2024-09-08
 */
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenuEntity> implements ISysMenuService {

    private static final String MENU_CODE_SPLIT = "_";

    @Override
    public List<MenuResDTO> selectList(MenuReqDTO reqDTO) {
        return baseMapper.selectMenuList(reqDTO);
    }

    @Override
    public PageVO<MenuResDTO> selectPage(MenuReqDTO reqDTO) {
        if (Objects.nonNull(reqDTO.getParentId())) {
            SysMenuEntity sysMenuEntity = this.getById(reqDTO.getParentId());
            reqDTO.setMenuCode(sysMenuEntity.getMenuCode());
        }
        PageVO<MenuResDTO> page =  PageUtil.convertPages(reqDTO, baseMapper::selectMenuList);
        List<MenuResDTO> list = page.getRoot();
        if (CollectionUtils.isEmpty(list)) {
            return page;
        }
        // 完善路径信息
        List<String> menuCodeList = list.stream().map(MenuResDTO::getMenuCode).collect(Collectors.toList());
        List<SysMenuEntity> menuList = selectAllMenuByMenuCode(menuCodeList);
        Map<String, SysMenuEntity> menuCodeMap = menuList.stream().collect(Collectors.toMap(SysMenuEntity::getMenuCode, Function.identity()));
        list.forEach(item -> {
            List<String> split = Arrays.asList(item.getMenuCode().split(MENU_CODE_SPLIT));
            List<String> parentMenuNameList = Lists.newArrayList();
            for (int i = 0; i < split.size(); i++) {
                SysMenuEntity sysMenuEntity = menuCodeMap.get(String.join(MENU_CODE_SPLIT, split.subList(0, i + 1)));
                if (Objects.nonNull(sysMenuEntity)) {
                    parentMenuNameList.add(sysMenuEntity.getMenuName());
                }
            }
            item.setFullMenuName(String.join("/", parentMenuNameList));
        });

        return page;
    }

    /**
     * 根据菜单code所有父菜单
     * @param menuCodeList
     * @return
     */
    private List<SysMenuEntity> selectAllMenuByMenuCode(List<String> menuCodeList) {
        Set<String> menuCodeSet = new HashSet<>();
        menuCodeList.forEach(menuCode -> {
            List<String> split = Arrays.asList(menuCode.split(MENU_CODE_SPLIT));
            for (int i = 0; i < split.size(); i++) {
                menuCodeSet.add(String.join(MENU_CODE_SPLIT, split.subList(0, i +1)));
            }
        });
        return this.lambdaQuery().in(SysMenuEntity::getMenuCode, new ArrayList<>(menuCodeSet)).list();
    }

    @Override
    public MenuDetailResDTO detail(Long id) {
        SysMenuEntity sysMenuEntity = this.getById(id);
        return MenuConvert.INSTANCE.detailConvert(sysMenuEntity);
    }


    private String generateMenuCode(Long parentId) {
        // 空为根目录
        parentId = Objects.nonNull(parentId) ? parentId : 0;
        String lastMenuCode = baseMapper.queryLastMenuCodeByParentId(parentId);
        if (StringUtils.isNotEmpty(lastMenuCode)) {
            String[] split = lastMenuCode.split(MENU_CODE_SPLIT);
            String code = split[split.length - 1];
            if (Objects.equals(parentId, 0L)) {
                return "" + (Integer.parseInt(code) + 1);
            }
            return lastMenuCode.substring(0, lastMenuCode.lastIndexOf(code)) + (Integer.parseInt(code) + 1);
        } else {
            if (Objects.equals(parentId, 0L)) {
                return "1";
            }
            SysMenuEntity parentMenu = this.getById(parentId);
            return parentMenu.getMenuCode() + MENU_CODE_SPLIT + "1";
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveMenu(MenuEditReqDTO reqDTO) {
        SysMenuEntity sysMenuEntity = MenuConvert.INSTANCE.editReqDTOConvert(reqDTO);
        sysMenuEntity.setId(IdWorker.getId());
        sysMenuEntity.setMenuCode(generateMenuCode(reqDTO.getParentId()));
//        sysMenuEntity.setCreateBy(UserHolder.getUserId());
//        sysMenuEntity.setCreateByName(UserHolder.getUserName());
//        sysMenuEntity.setCreateTime(LocalDateTime.now());
//        sysMenuEntity.setUpdateBy(UserHolder.getUserId());
//        sysMenuEntity.setUpdateByName(UserHolder.getUserName());
        sysMenuEntity.setUpdateTime(LocalDateTime.now());
        this.save(sysMenuEntity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateMenu(MenuEditReqDTO reqDTO) {
        LocalDateTime now = LocalDateTime.now();
        SysMenuEntity oldEntity = this.getById(reqDTO.getId());
        SysMenuEntity updateEntity = MenuConvert.INSTANCE.editReqDTOConvert(reqDTO);
        if (!Objects.equals(oldEntity.getParentId(), updateEntity.getParentId())) {
            String menuCode = generateMenuCode(updateEntity.getParentId());
            updateEntity.setMenuCode(menuCode);
            // 同步更新子菜单编号
            List<SysMenuEntity> list = this.lambdaQuery().like(SysMenuEntity::getMenuCode, oldEntity.getMenuCode() + "%").list();
            Map<Long, List<SysMenuEntity>> parentIdGroup = list.stream().collect(Collectors.groupingBy(SysMenuEntity::getParentId));
            List<SysMenuEntity> updateMenuCodeList = getUpdateMenuCodeList(parentIdGroup, updateEntity.getId(), menuCode);
            for (SysMenuEntity item : updateMenuCodeList) {
//                item.setUpdateBy(UserHolder.getUserId());
//                item.setUpdateByName(UserHolder.getUserName());
                item.setUpdateTime(now);
            }
            this.updateBatchById(updateMenuCodeList);
        }
        updateEntity.setId(reqDTO.getId());
//        updateEntity.setUpdateBy(UserHolder.getUserId());
//        updateEntity.setUpdateByName(UserHolder.getUserName());
        updateEntity.setUpdateTime(now);
        this.updateById(updateEntity);
    }

    /**
     * 递归获取需要更新code的子菜单
     * @param parentIdGroup
     * @param parentId
     * @param parentMenuCode
     */
    private  List<SysMenuEntity> getUpdateMenuCodeList (Map<Long, List<SysMenuEntity>> parentIdGroup,
                                                        Long parentId, String parentMenuCode) {
        List<SysMenuEntity> list = parentIdGroup.get(parentId);
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyList();
        }
        int i = 1;
        List<SysMenuEntity> updateList = Lists.newArrayList();
        for (SysMenuEntity item : list) {
            String menuCode = parentMenuCode + MENU_CODE_SPLIT + i++;
            SysMenuEntity updateEntity = new SysMenuEntity();
            updateEntity.setId(item.getId());
            updateEntity.setMenuCode(menuCode);
            updateList.add(updateEntity);
            updateList.addAll(getUpdateMenuCodeList(parentIdGroup, item.getId(), menuCode));
        }
        return updateList;
    }

    @Override
    public void updateEnableStatus(MenuUpdateStatusReqDTO reqDTO) {
        SysMenuEntity oldEntity = this.getById(reqDTO.getId());
        SysMenuEntity updateEntity = new SysMenuEntity();
        updateEntity.setId(reqDTO.getId());
        updateEntity.setEnableStatus(reqDTO.getEnableStatus());
//        updateEntity.setUpdateBy(UserHolder.getUserId());
//        updateEntity.setUpdateByName(UserHolder.getUserName());
        updateEntity.setUpdateTime(LocalDateTime.now());
        this.updateById(updateEntity);
    }

    @Override
    public void delete(Long id) {
        SysMenuEntity oldEntity = this.getById(id);
        SysMenuEntity updateEntity = new SysMenuEntity();
        updateEntity.setId(id);
//        updateEntity.setUpdateBy(UserHolder.getUserId());
//        updateEntity.setUpdateByName(UserHolder.getUserName());
        updateEntity.setUpdateTime(LocalDateTime.now());
        this.updateById(updateEntity);
        this.removeById(id);
    }


}
